/*
** Text-Based Gaming System 
** Copyright (C) 2008 Eric Day
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to:
** Free Software Foundation, Inc.
** 51 Franklin Street, Fifth Floor,
** Boston, MA  02110-1301, USA
*/

#include "tgs.h"
#include "tgsd.h"

/* maximum number of players */
#define TGS_DRAW_MAX_PLAYERS 10

/* struct to keep track of client game data */
typedef struct _TGS_DRAW
{
    int input_state;
    int draw;
    int x;
    int y;
} TGS_DRAW;

/* struct to keep track of server game data */
typedef struct _TGSD_DRAW
{
    TGSD_CON *con[TGS_DRAW_MAX_PLAYERS];
    int draw;
    int x;
    int y;
} TGSD_DRAW;

/*
** static functions
*/

/* hooks for client interface */
static int _tgs_start(TGS *tgs);
static int _tgs_part(TGS *tgs);
static int _tgs_input(TUI *tui, void *data, int key);
static int _tgs_focus(TUI *tui, void *data);
static int _tgs_data(TGS *tgs, char *buf);

/* hooks for server interface */
static int _tgsd_start(TGSD_CON *con);
static int _tgsd_join(TGSD_CON *con);
static int _tgsd_part(TGSD_CON *con);
static int _tgsd_data(TGSD_CON *con, char *buf);

/* global variables */
TGS_GAME tgs_draw =
{
    "Draw",
    _tgs_start,
    _tgs_part,
    _tgs_input,
    _tgs_focus,
    _tgs_data,
};

TGSD_GAME tgsd_draw =
{
    "Draw",
    _tgsd_start,
    _tgsd_join,
    _tgsd_part,
    _tgsd_data
};

/*
** static functions
*/

/* hooks for game interface */
static int _tgs_start(TGS *tgs)
{
    TGS_DRAW *s;
    int       ret;

    /* allocate and save game struct */
    s = calloc(1, sizeof(TGS_DRAW));
    TGS_RET_CHECK(tgs, s == NULL, "calloc", errno);

    tgs->game_data = s;

    /* setup the ui hooks for input and focus */
    tui_set_app_handlers(tgs->tui, tgs, _tgs_input, _tgs_focus);

    /* set the game status bar */
    ret = tui_set_app_status(tgs->tui,
        "Waiting For Server To Start Draw Game", 1);
    TGS_RET_CHECK(tgs, ret, "tui_set_app_status", ret);

    /* clean up the game screen */
    ret = werase(tgs->game_win);
    TGS_RET_CHECK(tgs, ret == ERR, "werase", -1);

    ret = wrefresh(tgs->game_win);
    TGS_RET_CHECK(tgs, ret == ERR, "wrefresh", -1);

    return 0;
}

static int _tgs_part(TGS *tgs)
{
    TGS_DRAW *s = (TGS_DRAW *)(tgs->game_data);
    int       ret;

    /* set the default status string and clear the ui hooks */
    ret = tui_set_app_status(tgs->tui, TGS_DEFAULT_GAME_STATUS, 1);
    TGS_RET_CHECK(tgs, ret, "tui_set_app_status", ret);

    tui_set_app_handlers(tgs->tui, NULL, NULL, NULL);

    /* free game struct and remove references */
    tgs->game = NULL;

    if (tgs->game_data)
    {
        free(s);
        tgs->game_data = NULL;
    }

    return 0;
}

static int _tgs_input(TUI *tui, void *data, int key)
{
    TGS      *tgs = (TGS *)data;
    TGS_DRAW *s = (TGS_DRAW *)(tgs->game_data);

    switch (s->input_state)
    {
    case 0:
        if (key == 0x15)
        {
            /* CTRL-U */
            lpc_printf(tgs->lpc, "GAME CLEAR");
        }
        else if (key == 0x1B)
        {   
            /* ESC - start escape sequence for special keys */
            s->input_state = 1;
        }
        else if (key == 0x0A || key == 0x0D)
        {   
            /* ENTER/RETURN */
            s->draw = 1 - s->draw;
            lpc_printf(tgs->lpc, "GAME CHANGE 0 0 %d 0", s->draw);
        }
        else if (key >= 0x20 && key <= 0x7E)
        {
            /* PRINTABLE CHAR */
            lpc_printf(tgs->lpc, "GAME CHANGE 0 0 %d %d", s->draw, key);
            lpc_printf(tgs->lpc, "GAME CHANGE 1 0 %d 0", s->draw);
        }

        break;
    case 1:
        if (key == 0x5B)
            s->input_state = 2; /* ESC+ */
        else
            s->input_state = 0;

        break;

    case 2:
        if (key == 0x41)
        {
            /* UP ARROW */
            lpc_printf(tgs->lpc, "GAME CHANGE 0 -1 %d 0", s->draw);
        }
        else if (key == 0x42)
        {
            /* DOWN ARROW */
            lpc_printf(tgs->lpc, "GAME CHANGE 0 1 %d 0", s->draw);
        }
        else if (key == 0x43)
        {
            /* RIGHT ARROW */
            lpc_printf(tgs->lpc, "GAME CHANGE 1 0 %d 0", s->draw);
        }
        else if (key == 0x44)
        {
            /* LEFT ARROW */
            lpc_printf(tgs->lpc, "GAME CHANGE -1 0 %d 0", s->draw);
        }

        s->input_state = 0;

        break;

    default:
        s->input_state = 0;

        break;
    }

    return 0;
}

static int _tgs_focus(TUI *tui, void *data)
{
    TGS      *tgs = (TGS *)data;
    TGS_DRAW *s = (TGS_DRAW *)(tgs->game_data);
    int       ret;

    /* ignore errors, this function errors when touching the last column */
    wmove(tgs->game_win, s->y, s->x);

    ret = wrefresh(tgs->game_win);
    TGS_RET_CHECK(tgs, ret == ERR, "wrefresh", -1);

    return 0;
}

static int _tgs_data(TGS *tgs, char *buf)
{
    TGS_DRAW *s = (TGS_DRAW *)(tgs->game_data);
    int       ret;
    char     *tok;
    int       key;
    char      str[2];

    tok = strtok(buf, " \t");
    if (tok == NULL)
        return 0;

    if (!strcasecmp(tok, "ERR"))
    {
        tok = strtok(NULL, "");
        ret = tui_scroll_printf(tgs->tui, "*** Game Error: %s",
            tok ? tok : "-");
        TGS_RET_CHECK(tgs, ret, "tui_scroll_printf", ret);

        ret = _tgs_part(tgs);
        TGS_RET_CHECK(tgs, ret, "_tgs_part", ret);

        return 0;
    }
    else if (!strcasecmp(tok, "INIT"))
    {
        /* update the game status */
        ret = tui_set_app_status(tgs->tui, "Draw Game In Play", 1);
        TGS_RET_CHECK(tgs, ret, "tui_set_app_status", ret);

        /* fall through to drawing code */
    }
    else if (!strcasecmp(tok, "CHANGE"))
    {
        /* fall through to drawing code */
    }
    else if (!strcasecmp(tok, "CLEAR"))
    {
        /* clean up the game screen */
        ret = werase(tgs->game_win);
        TGS_RET_CHECK(tgs, ret == ERR, "werase", -1);

        ret = wrefresh(tgs->game_win);
        TGS_RET_CHECK(tgs, ret == ERR, "wrefresh", -1);

        return 0;
    }
    else
    {
        ret = tui_scroll_write(tgs->tui, "*** Invalid Server Command*");
        TGS_RET_CHECK(tgs, ret, "tui_scroll_write", ret);
        return 0;
    }

    /* grab x and y values and draw on screen */
    tok = strtok(NULL, " \t");
    TGS_TOK_CHECK(tgs, tok, "Invalid Server Command");

    s->x = atoi(tok);

    tok = strtok(NULL, " \t");
    TGS_TOK_CHECK(tgs, tok, "Invalid Server Command");

    s->y = atoi(tok);

    tok = strtok(NULL, " \t");
    TGS_TOK_CHECK(tgs, tok, "Invalid Server Command");

    s->draw = atoi(tok);

    tok = strtok(NULL, "");
    TGS_TOK_CHECK(tgs, tok, "Invalid Server Command");

    key = atoi(tok);

    if (!(s->draw) && key == 0)
        return 0;

    if (s->draw)
        ret = wattron(tgs->game_win, A_REVERSE | A_BOLD);
    else
        ret = wattron(tgs->game_win, A_BOLD);
    TGS_RET_CHECK(tgs, ret == ERR, "wattron", -1);

    if (s->draw && key == 0)
        str[0] = 0x20;
    else
        str[0] = key;
    str[1] = 0;

    /* ignore errors, this function errors when touching the last column */
    mvwaddnstr(tgs->game_win, s->y, s->x, str, 1);

    if (s->draw)
        ret = wattroff(tgs->game_win, A_REVERSE | A_BOLD);
    else
        ret = wattron(tgs->game_win, A_BOLD);
    TGS_RET_CHECK(tgs, ret == ERR, "wattroff", -1);

    ret = wrefresh(tgs->game_win);
    TGS_RET_CHECK(tgs, ret == ERR, "wrefresh", -1);

    return 0;
}

/* hooks for game interface */
static int _tgsd_start(TGSD_CON *con)
{
    TGSD_DRAW *s;
    int        ret;

    /* allocate and save game struct */
    s = calloc(1, sizeof(TGSD_DRAW));
    TGSD_RET_CHECK(s == NULL, "calloc", errno);

    con->gs->data = s;

    ret = _tgsd_join(con);
    TGSD_RET_CHECK(ret, "_tgsd_join", ret);

    return 0;
}

static int _tgsd_join(TGSD_CON *con)
{
    TGSD_DRAW *s = (TGSD_DRAW *)(con->gs->data);
    int        x;

    /* try to find an empty slot in the player list */
    for (x = 0; x < TGS_DRAW_MAX_PLAYERS; x++)
    {
        if (s->con[x] == NULL)
        {
            s->con[x] = con;
            lpc_printf(con->lpc, "GAME INIT %d %d %d 0", s->x, s->y, s->draw);
            return 0;
        }
    }

    lpc_printf(con->lpc, "GAME ERR Too many players");

    return 0;
}

static int _tgsd_part(TGSD_CON *con)
{
    TGSD_DRAW *s = (TGSD_DRAW *)(con->gs->data);
    int        x;
    int        ref = 0;

    /* remove player from list */
    for (x = 0; x < TGS_DRAW_MAX_PLAYERS; x++)
    {
        if (s->con[x] == con)
            s->con[x] = NULL;

        if (s->con[x] != NULL)
            ref++;
    }

    /* if no more players are left, free and remove session */
    if(!ref)
    {
        free(s);
        con->gs->game = NULL;
        con->gs->data = NULL;
    }
 
    con->gs = NULL;

    return 0;
}

static int _tgsd_data(TGSD_CON *con, char *buf)
{
    TGSD_DRAW *s = (TGSD_DRAW *)(con->gs->data);
    char      *tok;
    int        x;
    int        key;

    tok = strtok(buf, " \t");
    if (tok == NULL)
        return 0;

    if (!strcasecmp(tok, "CHANGE"))
    {
        /* apply x and y changes and verify values */
        tok = strtok(NULL, " \t");
        TGSD_TOK_CHECK(con, tok, "GAME ERR Invalid GAME Command");

        s->x += atoi(tok);
        if (s->x < 0)
            s->x = 0;
        if (s->x > 79)
            s->x = 79;

        tok = strtok(NULL, " \t");
        TGSD_TOK_CHECK(con, tok, "GAME ERR Invalid GAME Command");

        s->y += atoi(tok);
        if (s->y < 0)
            s->y = 0;
        if (s->y > TGS_GAME_WIN_HEIGHT - 1)
            s->y = TGS_GAME_WIN_HEIGHT - 1;

        tok = strtok(NULL, " \t");
        TGSD_TOK_CHECK(con, tok, "GAME ERR Invalid GAME Command");

        s->draw = atoi(tok);
        if ((s->draw & ~1) != 0)
            s->draw = 0;

        tok = strtok(NULL, "");
        TGSD_TOK_CHECK(con, tok, "GAME ERR Invalid GAME Command");

        key = atoi(tok);

        /* send new x and y values to all players */
        for (x = 0; x < TGS_DRAW_MAX_PLAYERS; x++)
        {
            if (s->con[x] != NULL)
            {
                lpc_printf(s->con[x]->lpc, "GAME CHANGE %d %d %d %d", s->x,
                    s->y, s->draw, key);
            }
        }
    }
    else if (!strcasecmp(tok, "CLEAR"))
    {
        /* relay message to all players */
        for (x = 0; x < TGS_DRAW_MAX_PLAYERS; x++)
        {
            if (s->con[x] != NULL)
                lpc_printf(s->con[x]->lpc, "GAME CLEAR");
        }
    }
    else
        lpc_printf(con->lpc, "GAME ERR Invalid GAME Command");

    return 0;
}
